{% load filters %}

<script nonce="{{request.csp_nonce}}">

  const SERVER_ID = {{ request.server_id|json_dumps_ensure_ascii|safe }};
  const EVENT_NAMESPACE_KEY = "heidi_tips";
  const CACHE_KEY = "heidi_live_tips_collection";
  const CACHE_FETCHED_AT_KEY = "heidi_live_tips_collection_fetched_at";
  const CACHE_STALE_TIME = 1000 * 60 * 60; // 1 hour
  const MAX_TIMEOUT = 5000; // 5 seconds
  const TIP_COLLECTION_NAME = "authPage";

  const defaultTipsCollection = {
    authPage: [
      {
        title: "Did you know?",
        description:
          "You can sync data from all popular cloud storage providers to collect new items for labeling as they are uploaded, and return the annotation results to train and continuously improve models.",
        link: {
          label: "Learn more",
          url: "https://labelstud.io/guide/storage",
          params: {
            experiment: "login_revamp",
            treatment: "sync_cloud_data",
          },
        },
      },
      {
        title: "Did you know?",
        description:
          "There's an Enterprise version of Label Studio packed with more features and automation to label data faster while ensuring the highest quality.",
        link: {
          label: "Learn more",
          url: "https://humansignal.com/goenterprise/",
          params: {
            experiment: "login_revamp",
            treatment: "enterprise_platform",
          },
        },
      },
      {
        title: "Did you know?",
        description:
          "Label Studio has dozens of pre-built templates for all data types you can use to configure your labeling UI, from image classification to sentiment analysis to supervised LLM fine-tuning. ",
        link: {
          label: "See all templates",
          url: "https://labelstud.io/templates",
          params: {
            experiment: "login_revamp",
            treatment: "templates",
          },
        },
      },
      {
        title: "Did you know?",
        description: "Label Studio now has a Starter Cloud offering optimized for small teams and projects.",
        link: {
          label: "Learn more",
          url: "https://humansignal.com/pricing/",
          params: {
            experiment: "login_revamp",
            treatment: "starter_cloud"
          }
        }
      },
    ],
  };

  function createURL(url, params) {
    const base = new URL(url);

    Object.entries(params ?? {}).forEach(([key, value]) => {
      base.searchParams.set(key, value);
    });

    if (SERVER_ID) base.searchParams.set("server_id", SERVER_ID);

    return base.toString()
  }

  const loadLiveTipsCollection = () => {
    // stale while revalidate - we will return the data present in the cache or the default data and fetch updated data to be put into the cache for the next time this function is called without waiting for the promise.
    const cachedData = localStorage.getItem(CACHE_KEY);
    const fetchedAt = localStorage.getItem(CACHE_FETCHED_AT_KEY);

    // Read from local storage if the cachedData is less than CACHE_STALE_TIME milliseconds old
    if (cachedData && fetchedAt && Date.now() - Number.parseInt(fetchedAt) < CACHE_STALE_TIME) {
      return JSON.parse(cachedData);
    }

    const abortController = new AbortController();

    // Abort the request after MAX_TIMEOUT milliseconds to ensure we won't wait for too long, something might be wrong with the network or it could be an air-gapped instance
    const abortTimeout = setTimeout(abortController.abort, MAX_TIMEOUT);

    // Fetch from github raw liveContent.json proxied through the server
    fetch("/heidi-tips", {
      headers: {
        "Cache-Control": "no-cache",
        "Content-Type": "application/json",
      },
      signal: abortController.signal,
    })
      .then(async (response) => {
        if (response.ok) {
          const data = await response.json();

          // Cache the fetched content
          localStorage.setItem(CACHE_FETCHED_AT_KEY, String(Date.now()));
          localStorage.setItem(CACHE_KEY, JSON.stringify(data));
        }
      })
      .catch((e) => {
        console.warn("Failed to load live Heidi tips collection", e);
      })
      .finally(() => {
        // Wait until the content is fetched to clear the abort timeout
        // The abort should consider the entire request not just the headers
        clearTimeout(abortTimeout);
      });

    // Serve possibly stale cached content
    if (cachedData) {
      return JSON.parse(cachedData);
    }

    // Default local content
    return defaultTipsCollection;
  };

  function getRandomTip() {
    const tipsCollection = loadLiveTipsCollection();

    if (!tipsCollection[TIP_COLLECTION_NAME]) return null;

    const tips = tipsCollection[TIP_COLLECTION_NAME];

    const index = Math.floor(Math.random() * tips.length);

    return tips[index];
  }

  function getTipCollectionEvent(collection, event) {
    return `${EVENT_NAMESPACE_KEY}.${collection}.${event}`;
  }

  function getTipEvent(collection, tip, event) {
    if (tip.link.params?.experiment && tip.link.params?.treatment) {
      return `${EVENT_NAMESPACE_KEY}.${collection}.${tip.link.params?.experiment}.${tip.link.params?.treatment}.${event}`;
    }
    if (tip.link.params?.experiment) {
      return `${EVENT_NAMESPACE_KEY}.${collection}.${tip.link.params?.experiment}.${event}`;
    }
    if (tip.link.params?.treatment) {
      return `${EVENT_NAMESPACE_KEY}.${collection}.${tip.link.params?.treatment}.${event}`;
    }

    return getTipCollectionEvent(collection, event);
  }

  function getTipMetadata(tip) {
    // Everything except the experiment and treatment params as those are part of the event name
    const { experiment, treatment, ...rest } = tip.link.params ?? {};
    return {
      ...rest,
      content: tip.description ?? tip.content ?? "",
      title: tip.title,
      href: tip.link.url,
      label: tip.link.label,
    };
  }

  document.addEventListener('DOMContentLoaded', function() {
    const _container = document.querySelector('.tips');
    const _title = document.querySelector('.tips .title');
    const _description = document.querySelector('.tips .description');

    const selectedTip = getRandomTip();
    if (!selectedTip) {
      // Remove the tips container if there are no tips to show
      _container.remove();
      return;
    }

    // Add the click handler to all links inside the tips container that have the data-heidi-tip-link attribute
    // In the future if there are more than one tip, we will need to check which tip was clicked
    _container.addEventListener('click', (event) => {
      // If the clicked element is the link, log the click
      if (event.target.hasAttribute('data-heidi-tip-link')) {
        __lsa(getTipEvent(TIP_COLLECTION_NAME, selectedTip, 'click'), getTipMetadata(selectedTip));
      }
    });

    const linkUrl = createURL(selectedTip.link.url, selectedTip.link.params)

    _title.innerHTML = selectedTip.title;
    _description.innerHTML = `${selectedTip.description} <a data-heidi-tip-link href="${linkUrl}" target="_blank">${selectedTip.link.label}</a>`;
  });
</script>

<div class="tips">
  <div class="title"></div>
  <div class="description"></div>
</div>
